// Copyright 2006 Bret Taylor
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

#ifndef INDIE_CHECKER_H__
#define INDIE_CHECKER_H__

#include <map>
#include <set>
#include <string>

#include "ast.h"
#include "log.h"
#include "type.h"
#include "visitor.h"

using std::map;
using std::multimap;
using std::set;
using std::string;

namespace indie {

class Checker : private Visitor {
  public:
    Checker(Log& log);
    ~Checker();

    void Check(const set<AstFile*>& files);

  private:
    void LoadClasses(const set<AstFile*>& files);
    void LoadMethods(ClassType& cls, set<const ClassType*>& loaded);
    void CheckCode();
    void Cleanup();

    // Returns the class with the given name
    const ClassType* LookupClass(const string& name);

    // Type lookups that map ASTs to Types
    void SetTreeType(const Ast& ast, const UnionType& type);
    const UnionType& GetTreeType(const Ast& ast);
    void SetType(const AstType& ast, const Type& type);
    const Type* GetType(const AstType& ast);

    // Variable scoping
    class ScopedVariable;
    void PushScope();
    void PopScope();
    void RegisterVariable(const AstVariable& variable);
    const AstVariable* LookupVariable(const string& name);

    // Variable scope popping/pushing
    virtual void Enter(const AstMethod& ast);
    virtual void Leave(const AstMethod& ast);
    virtual void Enter(const AstFor& ast);
    virtual void Leave(const AstFor& ast);
    virtual void Enter(const AstBlockStatement& ast);
    virtual void Leave(const AstBlockStatement& ast);

    // Type-loading for types and variables
    virtual void Leave(const AstVariable& ast);
    virtual void Leave(const AstClassType& ast);
    virtual void Leave(const AstArrayType& ast);
    virtual void Leave(const AstUnionType& ast);

    // Expression type-checking
    virtual void Leave(const AstParenthesizedExpression& ast);
    virtual void Leave(const AstAccess& ast);
    virtual void Leave(const AstArrayAccess& ast);
    virtual void Leave(const AstBinaryExpression& ast);
    virtual void Leave(const AstUnaryExpression& ast);
    virtual void Leave(const AstCall& ast);
    virtual void Leave(const AstNewInstance& ast);
    virtual void Leave(const AstNewArray& ast);
    virtual void Leave(const AstTypeTest& ast);
    virtual void Leave(const AstThis& ast);
    virtual void Leave(const AstIntegerConstant& ast);
    virtual void Leave(const AstStringConstant& ast);
    virtual void Leave(const AstFloatConstant& ast);
    virtual void Leave(const AstBooleanConstant& ast);

    // Statement type-checking
    virtual void Leave(const AstIf& ast);
    virtual void Leave(const AstWhile& ast);
    virtual void Leave(const AstAssignment& ast);
    virtual void Leave(const AstVariableDeclaration& ast);
    virtual void Leave(const AstReturn& ast);

    void CheckAssignment(const Ast& left_side, const Ast& right_side);

    inline const ClassType& current_class_type() {
	assert(current_class_type_ != NULL);
	return *current_class_type_;
    }

    inline const AstClass& current_class() {
	assert(current_class_ != NULL);
	return *current_class_;
    }

    inline const AstMethod& current_method() {
	assert(current_method_ != NULL);
	return *current_method_;
    }

    Log& log_;

    int current_scope_;
    multimap<string, ScopedVariable*> scoped_variables_;

    const ClassType* current_class_type_;
    const AstClass* current_class_;
    const AstMethod* current_method_;

    map<string, const ClassType*> built_ins_;
    map<string, ClassType*> classes_;

    map<const Ast*, const UnionType*> tree_types_;
    map<const AstType*, const Type*> types_;
};

}

#endif // INDIE_CHECKER_H__
